package jtimebomb;

/*
 * JTImeBombFrame.java
 *
 * Created on 28. Juli 2008, 12:22
 */
import java.awt.Color;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.PushbackInputStream;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ResourceBundle;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.SpinnerNumberModel;

/**
 *
 * @author  uli
 */
public class JTimeBombFrame extends javax.swing.JFrame {
    
    //Custom components
    int totalSecondsLeft; //In seconds, overall
    int hoursLeft;
    int minutesLeft; //Not overall
    int secondsLeft; //Not overall
    Timer timer;
    private JTimeBombClientFrame clientFrame;
    
    //Internationalization
    private final static ResourceBundle i18n = ResourceBundle.getBundle("jtimebomb/i18n");
   
    /** Creates new form JTImeBombFrame */
    public JTimeBombFrame() {
        initComponents();
        //Init client frame but don't show
        clientFrame = new JTimeBombClientFrame();
        //Init server
        Thread t = new Thread(new timeBombServerThread());
        t.start();
    }

    private void activate() {
        //Get data
        SpinnerNumberModel model = (SpinnerNumberModel) hourSpinner.getModel();
            hoursLeft = model.getNumber().intValue();
        model = (SpinnerNumberModel) minuteSpinner.getModel();
            minutesLeft = model.getNumber().intValue();
        model = (SpinnerNumberModel) secondSpinner.getModel();
            secondsLeft = model.getNumber().intValue(); //+1: Start countdown at exactly the selected time and not at one second below
        totalSecondsLeft = secondsLeft + minutesLeft * 60 + hoursLeft * 3600 + 1; //+1: Start countdown at exactly the selected time and not at one second below
        //Initialize status bar
         statusBar.setMaximum(totalSecondsLeft);
         statusBar.setValue(totalSecondsLeft);
        //Initialize main (big) timer Label
         String timerString = Integer.toString(hoursLeft) + i18n.getString("Connect")
                                + Integer.toString(minutesLeft) + ":"
                                + Integer.toString(secondsLeft);

         timeLeftLabel.setText(timerString);
        //Set state label value and color
        statusLabel.setText(i18n.getString("Activated"));
        statusLabel.setForeground(new Color(255,0,0));
        //Activate timer
        timer = new Timer();
        timer.schedule(new TimerTask() { //Schedule timer to call timerTick every second
                                            public void run()
                                                {
                                                    timerTick();
                                                }
                                        }, 0, 1000);
    }

    private void defuse() {
          timer.cancel();
          statusLabel.setText(i18n.getString("Defused"));
          statusLabel.setForeground(new Color(0,255,0));
    }

    private void detonate() {
                if (timer != null) {timer.cancel();}
                statusLabel.setText(i18n.getString("Detonated"));
                activateToggleButton.setSelected(false);
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        timeLeftLabel = new javax.swing.JLabel();
        statusLabel = new javax.swing.JLabel();
        hourSpinner = new javax.swing.JSpinner();
        minuteSpinner = new javax.swing.JSpinner();
        secondSpinner = new javax.swing.JSpinner();
        hourLabel = new javax.swing.JLabel();
        minutesLabel = new javax.swing.JLabel();
        secondsLabel = new javax.swing.JLabel();
        statusBar = new javax.swing.JProgressBar();
        activateToggleButton = new javax.swing.JToggleButton();
        menuBar = new javax.swing.JMenuBar();
        extrasMenu = new javax.swing.JMenu();
        showClientMenuItem = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("jtimebomb/i18n"); // NOI18N
        setTitle(bundle.getString("Time_Bomb")); // NOI18N
        setBackground(new java.awt.Color(0, 0, 0));
        setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        setFocusCycleRoot(false);
        setForeground(java.awt.Color.black);
        setResizable(false);

        timeLeftLabel.setBackground(new java.awt.Color(0, 0, 0));
        timeLeftLabel.setFont(new java.awt.Font("Gunplay", 0, 36));
        timeLeftLabel.setForeground(new java.awt.Color(255, 0, 51));
        timeLeftLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        timeLeftLabel.setText("00:00:00"); // NOI18N
        timeLeftLabel.setFocusable(false);

        statusLabel.setFont(new java.awt.Font("Gunplay", 0, 18));
        statusLabel.setForeground(new java.awt.Color(0, 255, 0));
        statusLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        statusLabel.setText(bundle.getString("Deactivated")); // NOI18N

        hourSpinner.setModel(new SpinnerNumberModel(0, 0, 99, 1));
        hourSpinner.setToolTipText(bundle.getString("Hours_in_the_delay_timer")); // NOI18N

        minuteSpinner.setModel(new SpinnerNumberModel(0, -1, 60, 1));
        minuteSpinner.setToolTipText(bundle.getString("Minutes_in_the_delay_timer")); // NOI18N
        minuteSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                minuteSpinnerStateChanged(evt);
            }
        });

        secondSpinner.setModel(new SpinnerNumberModel(0, -1, 60, 1));
        secondSpinner.setToolTipText(bundle.getString("Seconds_in_the_delay_timer")); // NOI18N
        secondSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                secondSpinnerStateChanged(evt);
            }
        });

        hourLabel.setDisplayedMnemonic('u');
        hourLabel.setLabelFor(hourSpinner);
        hourLabel.setText(bundle.getString("Hours:")); // NOI18N

        minutesLabel.setDisplayedMnemonic('m');
        minutesLabel.setLabelFor(minuteSpinner);
        minutesLabel.setText(bundle.getString("Minutes:")); // NOI18N

        secondsLabel.setDisplayedMnemonic('s');
        secondsLabel.setLabelFor(secondSpinner);
        secondsLabel.setText(bundle.getString("Seconds:")); // NOI18N

        statusBar.setForeground(new java.awt.Color(255, 0, 51));

        activateToggleButton.setMnemonic('a');
        activateToggleButton.setText(bundle.getString("Activate")); // NOI18N
        activateToggleButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                activateToggleButtonMouseClicked(evt);
            }
        });

        extrasMenu.setText(i18n.getString("Extras")); // NOI18N

        showClientMenuItem.setText(i18n.getString("Show_client")); // NOI18N
        showClientMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showClientMenuItemActionPerformed(evt);
            }
        });
        extrasMenu.add(showClientMenuItem);

        menuBar.add(extrasMenu);

        setJMenuBar(menuBar);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(activateToggleButton, javax.swing.GroupLayout.PREFERRED_SIZE, 275, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(timeLeftLabel)
                            .addComponent(statusBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(statusLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 135, Short.MAX_VALUE)
                            .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                                .addGap(9, 9, 9)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                    .addComponent(secondsLabel)
                                    .addComponent(minutesLabel)
                                    .addComponent(hourLabel))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(minuteSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(hourSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(secondSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 10, Short.MAX_VALUE)))))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(statusLabel)
                        .addGap(32, 32, 32)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(minutesLabel)
                            .addComponent(minuteSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(secondsLabel)
                            .addComponent(secondSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(timeLeftLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 62, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(hourSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(hourLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(statusBar, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(activateToggleButton)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void activateToggleButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_activateToggleButtonMouseClicked
        // TODO add your handling code here:
        //Check whether to activate or to deactivate the bomb
        if(activateToggleButton.isSelected()) //Activate the bomb
            {
                activate();
            }
        else //Try to activate the bomb
            {
                defuse();
            }
    }//GEN-LAST:event_activateToggleButtonMouseClicked

    private void secondSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_secondSpinnerStateChanged
        //If seconds are 60, set seconds to 0 and increase minutes
        SpinnerNumberModel secondModel = (SpinnerNumberModel) secondSpinner.getModel();
        SpinnerNumberModel minuteModel = (SpinnerNumberModel) minuteSpinner.getModel();
        SpinnerNumberModel hourModel = (SpinnerNumberModel) hourSpinner.getModel();
        if(secondModel.getNumber().intValue() >= 60)
            {
                secondModel.setValue(0);
                minuteModel.setValue(minuteModel.getNextValue());
                return; //Value cannot be greater than 60 and less than 0 at one time
            }
        //If seconds are less than 0 set seconds to 0 and increase minutes
        if(secondModel.getNumber().intValue() <= -1)
            {
                //If the overall time is 0 (no minute to split into seconds), set seconds to 0 and return
                if(minuteModel.getNumber().intValue() == 0 && hourModel.getNumber().intValue() == 0)
                    {
                        secondModel.setValue(0);
                        return;
                    }
                secondModel.setValue(59);
                minuteModel.setValue(minuteModel.getPreviousValue());
            }
    }//GEN-LAST:event_secondSpinnerStateChanged

    class timeBombServerThread implements Runnable //Subclass
    {
    ServerSocket server;
    Socket socket;
    PushbackInputStream in;
    PrintWriter outWriter;
    int port = 12121;
    public void run() {
        try
            {
                server = new ServerSocket(port);
                char c;
                while(true)
                {
                socket = server.accept(); //Get client connection socket
                in = new PushbackInputStream(socket.getInputStream());
                outWriter = new PrintWriter(socket.getOutputStream(), true);
                //Print welcome message
                outWriter.write(java.util.ResourceBundle.getBundle("jtimebomb/i18n").getString("WelcomeMessage"));
                outWriter.flush();
                //Read data char-per-char until eof is read
                  while((c = (char) in.read()) != -1)
                    {
                        switch(c)
                        {
                            case 'd'/*detonate*/:
                                    {
                                        detonate();
                                        outWriter.write(java.util.ResourceBundle.getBundle("jtimebomb/i18n").getString("BombDetonatedMessage"));
                                        outWriter.flush();
                                        break;
                                    }
                            case 'm'/*decrease timer*/:
                                    {
                                        StringBuilder sb = new StringBuilder();
                                        c = (char) in.read();
                                        while(Character.isDigit(c))
                                            {
                                                sb.append(c);
                                                c = (char)in.read();
                                            }
                                        if(sb.toString().equals("")) //If the user has not entered a value
                                            {
                                                outWriter.write(i18n.getString("Usage:_m[number_of_seconds]"));
                                                outWriter.flush();
                                                continue;
                                            }
                                        totalSecondsLeft -= new Integer(sb.toString());
                                        outWriter.write(i18n.getString("Time_decreased_by_") + sb.toString() + i18n.getString("_seconds\n"));
                                        outWriter.flush();
                                        break;
                                     }
                            case 'i'/*increase timer*/:
                                    {
                                        StringBuilder sb = new StringBuilder();
                                        c = (char) in.read();
                                        while(Character.isDigit(c)) //c is digit in unicode
                                            {
                                                sb.append(c);
                                                c = (char)in.read();
                                            }
                                        in.unread(c);
                                        if(sb.toString().equals("")) //If the user has not entered a value
                                            {
                                                outWriter.write(i18n.getString("Usage:_i[number_of_seconds]\n"));
                                                outWriter.flush();
                                                continue;
                                            }
                                        totalSecondsLeft += new Integer(sb.toString());
                                        outWriter.write(i18n.getString("Time_increased_by_") + sb.toString() + i18n.getString("_seconds\n"));
                                        outWriter.flush();
                                        break;
                                     }
                            case 'h'/*Defuse*/:{
                                        defuse();
                                        outWriter.write(i18n.getString("Bomb_defused\n"));
                                        outWriter.flush();                                      
                                        break;
                                    }
                            case 'a'/*Activate*/:{
                                        activate();
                                        outWriter.write(i18n.getString("Bomb_activated\n"));
                                        outWriter.flush();
                                        break;
                                    }
                            case 's'/*Set timer*/:
                                    {
                                        StringBuilder sb = new StringBuilder();
                                        c = (char) in.read();
                                        while(Character.isDigit(c)) //c is digit in unicode
                                            {
                                                sb.append(c);
                                                c = (char)in.read();
                                            }
                                        in.unread(c);
                                        if(sb.toString().equals("")) //If the user has not entered a value
                                            {
                                                outWriter.write(i18n.getString("Usage:_s[number_of_seconds]\n"));
                                                outWriter.flush();
                                                continue;
                                            }
                                        totalSecondsLeft = new Integer(sb.toString());
                                        outWriter.write(i18n.getString("Time_set_to_") + sb.toString() + i18n.getString("_seconds\n"));
                                        outWriter.flush();
                                        break;
                                     } 
                            case 'p'/*Print status*/:
                                     {
                                        outWriter.write(timeLeftLabel.getText() + "\n");
                                        outWriter.flush();
                                     }
                            case 'q'/*Quit console*/:
                                    {
                                        socket.close();
                                        return;
                                    }
                            default: break;
                        }
                    }
                in.close();
                outWriter.close();
                socket.close();
                }
            }
        catch (BindException ex)
                {
                     String portString = JOptionPane.showInputDialog(null, i18n.getString("The_default_port_is_already_in_use._Select_a_new_port:"), i18n.getString("Port_already_in_use"), JOptionPane.QUESTION_MESSAGE);
                     if(!portString.equals("")) {port = new Integer(portString);}
                     run();
                }
        catch (IOException ex)
                {
                    Logger.getLogger(JTimeBombFrame.class.getName()).log(Level.SEVERE, null, ex);
                }
    }
    }
    
    private void minuteSpinnerStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_minuteSpinnerStateChanged
        //If seconds are 60, set seconds to 0 and increase minutes
        SpinnerNumberModel minuteModel = (SpinnerNumberModel) minuteSpinner.getModel();
        SpinnerNumberModel hourModel = (SpinnerNumberModel) hourSpinner.getModel();
        if(minuteModel.getNumber().intValue() >= 60)
            {
                minuteModel.setValue(0);
                hourModel.setValue(hourModel.getNextValue());
                return; //Value cannot be greater than 60 and less than 0 at one time
            }
        //If minutes are -1, set minutes to 0 and increase hours
        if(minuteModel.getNumber().intValue() <= -1)
            {
                //If the number of hours is null, set minutesValue to 0 and return.
                if(hourModel.getNumber().intValue() == 0)
                    {
                        minuteModel.setValue(0);
                        return;
                    }
                minuteModel.setValue(59);
                hourModel.setValue(hourModel.getPreviousValue());
            }
        updateTimeLeftLabel();
    }//GEN-LAST:event_minuteSpinnerStateChanged

    private void showClientMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showClientMenuItemActionPerformed
        clientFrame.setVisible(true);
    }//GEN-LAST:event_showClientMenuItemActionPerformed
    
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new JTimeBombFrame().setVisible(true);
            }
        });
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JToggleButton activateToggleButton;
    private javax.swing.JMenu extrasMenu;
    private javax.swing.JLabel hourLabel;
    private javax.swing.JSpinner hourSpinner;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JSpinner minuteSpinner;
    private javax.swing.JLabel minutesLabel;
    private javax.swing.JSpinner secondSpinner;
    private javax.swing.JLabel secondsLabel;
    private javax.swing.JMenuItem showClientMenuItem;
    private javax.swing.JProgressBar statusBar;
    private javax.swing.JLabel statusLabel;
    private javax.swing.JLabel timeLeftLabel;
    // End of variables declaration//GEN-END:variables

    private void updateTimeLeftLabel()
    {
        //Calculate time left
        secondsLeft = totalSecondsLeft%60;
        hoursLeft = (int) Math.floor(totalSecondsLeft/3600);
        minutesLeft = ((totalSecondsLeft%3600)-secondsLeft)/60;
        statusBar.setValue(totalSecondsLeft);
        //Initialize main (big) timer Label
        String timerString = Integer.toString(hoursLeft) + ":" + Integer.toString(minutesLeft) + ":" + Integer.toString(secondsLeft);
        timeLeftLabel.setText(timerString);
    }
    public void timerTick()
    {
        totalSecondsLeft--;
        if(totalSecondsLeft==0) //detonate bomb
            {
                detonate();
            }
    }    
}
